package app;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedList;

import javax.swing.text.MutableAttributeSet;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.parser.ParserDelegator;

import org.commons.HTTPUtils;
import org.commons.Utils;

import app.structure.Graph;


public class Spider {

	private String graphFilename = "graph.ser";

	private Graph graph;
	private String relativeCurrentAsString;
	private URL currentUrl;
	private LinkedList<Integer> toDoQueue;

	public Spider(URL startPage) {
		String startPageAsString = startPage.toString();
		String prefix = new String();
		String suffix = new String();
		int lastSlash = startPageAsString.lastIndexOf("/");
		if(lastSlash == startPageAsString.length() - 1)
			prefix = startPage.toString();
		else {
			int lastDot = startPageAsString.lastIndexOf(".");
			if(lastDot > lastSlash) {
				prefix = startPageAsString.substring(0, lastSlash + 1);
				suffix = startPageAsString.substring(lastSlash + 1, startPageAsString.length());
			}
			else
				prefix = startPage.toString().concat("/");
		}

		this.graph = new Graph(prefix, suffix);
		this.toDoQueue = new LinkedList<Integer>();
		this.relativeCurrentAsString = new String();
		this.currentUrl = startPage;
	}

	public Graph generateGraph() throws IOException {
		toDoQueue.addLast(graph.getStartHash());
		graph.insertAdjacentNodes(graph.getStartPath(), null);

		Reader reader = null;

		while(toDoQueue.size() > 0) {
			System.out.println("queue = " + toDoQueue.size());

			Integer urlHash = toDoQueue.pollFirst();
			relativeCurrentAsString = graph.getRelativePath(urlHash);
			currentUrl = new URL(graph.getPrefix() + relativeCurrentAsString);
			
			reader = new InputStreamReader((InputStream) currentUrl.getContent());
			new ParserDelegator().parse(reader, new LinkPage(), true);
			reader.close();
		}

		return graph;
	}

	class LinkPage extends HTMLEditorKit.ParserCallback {

		public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) {
			if (t == HTML.Tag.A) {
				Object href = a.getAttribute(HTML.Attribute.HREF);
				
				if (href != null && !href.toString().startsWith("#")) {
					String adjacentUrlAsString = new String();
					try {
						URL adjacentUrl = new URL(currentUrl, href.toString());
						adjacentUrlAsString = adjacentUrl.toString();
						
						if(adjacentUrlAsString.startsWith(graph.getPrefix())) {
							adjacentUrlAsString = adjacentUrlAsString.substring(graph.getPrefix().length());
							
							if(!adjacentUrlAsString.equals(relativeCurrentAsString) && HTTPUtils.exists(adjacentUrl)) {
								Integer relativeAdjacentHash = Utils.hash(adjacentUrlAsString);
								if(!graph.getHashPathMap().containsKey(relativeAdjacentHash)) {
									toDoQueue.addLast(relativeAdjacentHash);
								}
								graph.insertAdjacentNodes(relativeCurrentAsString, adjacentUrlAsString);
							}
						}
					} catch (MalformedURLException e) {
						// 
					}
				}
			}
		}
	}

	public boolean saveGraph() throws IOException {
		FileOutputStream fos;
		ObjectOutputStream oos;
		fos = new FileOutputStream(graphFilename);
		oos = new ObjectOutputStream(fos);
		oos.writeObject(graph);
		oos.close();
		fos.close();
		return true;
	}

	public Graph loadGraph() throws IOException {
		File f1 = new File(graphFilename);
		if(f1.exists()) {
			try {
				FileInputStream fis = new FileInputStream(graphFilename);
				ObjectInputStream ois = new ObjectInputStream(fis);
				graph = (Graph) ois.readObject();
				ois.close();
				fis.close();
			} catch (ClassNotFoundException e) {
				graph = null;
				return null;
			}
		}
		else {
			graph = null;
			throw new FileNotFoundException();
		}
		return graph;
	}

	public Graph getGraph() {
		return graph;
	}

	public String toString() {
		return "Root: "+ graph.getPrefix() + "\n" +graph.toString();
	}
}
